package com.zdp.applet.service;

import com.zdp.applet.mapper.ItemsSpecMapper;
import com.zdp.applet.mapper.ItemsSpecMapperCustom;
import com.zdp.common.constant.RedisConstants;
import com.zdp.common.domain.ItemsSpec;
import com.zdp.common.domain.vo.StockVo;
import com.zdp.common.exception.ParameterException;
import com.zdp.common.utils.JsonUtils;
import com.zdp.common.utils.RedisOperator;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author sesshomaru
 * @date 2021/7/10 22:26
 */
@Slf4j
@Service
public class ItemSpecService {

    @Autowired
    private RedisOperator redis;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private ItemsSpecMapper itemsSpecMapper;
    @Autowired
    private ItemsSpecMapperCustom itemsSpecMapperCustom;

    private static final String remainField = RedisConstants.STOCK_KEY_FIELD_REMAIN;
    private static final String freezeField = RedisConstants.STOCK_KEY_FIELD_FREEZE;

    // 后台追加库存 number为正数就是添加库存 为负数就是减少库存
    public void addStock(String itemSpecId, Integer number) {
        log.info("进入商品追加库存 itemSpecId: {}, buyCounts: {}", itemSpecId, number);
        String stockLock = String.format(RedisConstants.LOCK_STOCK_KEY, itemSpecId);
        RLock lock = redissonClient.getLock(stockLock);
        try {
            lock.lock(30, TimeUnit.SECONDS);
            log.info("商品追加库存加锁处理中itemSpecId: {}", itemSpecId);
            StockVo stockVo = getStockFromRedis(itemSpecId);
            if (number == 0) {
                throw new ParameterException(500, "追加库存数不可为0");
            } else if (number < 0) {
                //库存数为负数，减少库存
                if (stockVo.getRemainStock() < -number) {
                    throw new ParameterException(500, "商品规格编码itemSpecId=" + itemSpecId + "剩余库存数不够减少," +
                            "当前剩余库存数" + stockVo.getRemainStock());
                }
            }
            String hashKey = String.format(RedisConstants.STOCK_KEY, itemSpecId);
            redis.hashIncrement(hashKey, remainField, number);
            stockVo.setRemainStock(stockVo.getRemainStock() + number);
            log.info("商品追加库存redis处理成功 stockVo: {}", JsonUtils.objectToPrettyJson(stockVo));
            // TODO 异步添加或扣减库存数
        } finally {
            lock.unlock();
        }
    }

    // 下单冻结库存
    @Transactional(rollbackFor = Exception.class)
    public StockVo freezeStockFromRemain(String itemSpecId, Integer number) {
        log.info("进入购买商品冻结库存 itemSpecId: {}, number: {}", itemSpecId, number);
        String stockLock = String.format(RedisConstants.LOCK_STOCK_KEY, itemSpecId);
        RLock lock = redissonClient.getLock(stockLock);
        try {
            lock.lock(30, TimeUnit.SECONDS);
            log.info("商品冻结库存加锁处理中 itemSpecId: {}", itemSpecId);
            StockVo stockVo = getStockFromRedis(itemSpecId);
            if (stockVo.getRemainStock() <= 0) {
                throw new ParameterException(500, "商品规格编码itemSpecId=" + itemSpecId + "库存不够 库存数= " + stockVo.getRemainStock());
            }
            if (number == 0) {
                throw new ParameterException(500, "下单冻结库存数不可为0");
            }
            if (stockVo.getRemainStock() < number) {
                throw new ParameterException(500, "商品规格编码itemSpecId=" + itemSpecId +
                        " 可用库存不够下单,剩余库存数为" + stockVo.getRemainStock());
            }
            String hashKey = String.format(RedisConstants.STOCK_KEY, itemSpecId);
            redis.hashIncrement(hashKey, remainField, number * -1);
            redis.hashIncrement(hashKey, freezeField, number);

            stockVo.setRemainStock(stockVo.getRemainStock() - number);
            stockVo.setFreezeStock(stockVo.getFreezeStock() + number);
//            log.info("商品下单冻结库存redis处理成功 stockVo: {}", JsonUtils.objectToPrettyJson(stockVo));
            return stockVo;
        } finally {
            lock.unlock();
            log.info("商品下单冻结库存加锁已解锁itemSpecId: {}", itemSpecId);
        }
    }

    // 取消订单释放冻结库存
    public StockVo unFreezeStockToRemain(String itemSpecId, Integer number) {
        log.info("进入商品订单取消释放冻结库存 itemSpecId: {}, number: {}", itemSpecId, number);
        String stockLock = String.format(RedisConstants.LOCK_STOCK_KEY, itemSpecId);
        RLock lock = redissonClient.getLock(stockLock);
        try {
            lock.lock(30, TimeUnit.SECONDS);
            log.info("商品释放冻结库存加锁处理中itemSpecId: {}", itemSpecId);
            StockVo stockVo = getStockFromRedis(itemSpecId);
            if (number == 0) {
                throw new ParameterException(500, "取消订单释放冻结库存数不可为0");
            }
            if (stockVo.getFreezeStock() < number) {
                throw new ParameterException(500, "冻结库存数为" + stockVo.getFreezeStock() + " 释放冻结库存不够");
            }
            String hashKey = String.format(RedisConstants.STOCK_KEY, itemSpecId);
            redis.hashIncrement(hashKey, remainField, number);
            redis.hashIncrement(hashKey, freezeField, number * -1);
            stockVo.setRemainStock(stockVo.getRemainStock() + number);
            stockVo.setFreezeStock(stockVo.getFreezeStock() - number);
            // TODO 异步更新库存
            return stockVo;
        } finally {
            lock.unlock();
            log.info("商品取消下单释放冻结库存加锁已解锁itemSpecId: {}", itemSpecId);
        }
    }

    // 下单支付成功，减少冻结库存

    // 从缓存中获取库存信息，不存在则同步库存信息到缓存中
    public StockVo getStockFromRedis(String itemSpecId) {
        log.info("获取商品规格 itemSpecId: {}", itemSpecId);
        String hashKey = String.format(RedisConstants.STOCK_KEY, itemSpecId);
        StockVo stockVo = new StockVo();
        if (!redis.hashKey(hashKey)) {
            log.info("redis中不存在商品库存 itemSpecId: {}", itemSpecId);
            ItemsSpec itemsSpec = itemsSpecMapper.selectById(itemSpecId);
            if (itemsSpec == null
                    || itemsSpec.getRemainStock() == null
                    || itemsSpec.getFreezeStock() == null) {
                throw new ParameterException(500, "商品规格id" + itemSpecId + "库存异常");
            }
            stockVo.setItemSpecId(itemSpecId);
            stockVo.setRemainStock(itemsSpec.getRemainStock());
            stockVo.setFreezeStock(itemsSpec.getFreezeStock());

            redis.hashIncrement(hashKey, remainField, stockVo.getRemainStock());
            redis.hashIncrement(hashKey, freezeField, stockVo.getFreezeStock());
            return stockVo;
        } else {
            Integer remainStock = (Integer) redis.hashGet(hashKey, remainField);
            Integer freezeStock = (Integer) redis.hashGet(hashKey, freezeField);
            if (remainStock == null || freezeStock == null) {
                log.error("商品itemSpecId: {},库存异常, remainStock: {}, freezeStock :{}", itemSpecId, remainStock, freezeStock);
                throw new ParameterException(500, "商品规格id" + itemSpecId + "库存异常");
            }
            log.info("redis中已存在商品库存 itemSpecId: {}", itemSpecId);
            stockVo.setItemSpecId(itemSpecId);
            stockVo.setRemainStock(remainStock);
            stockVo.setFreezeStock(freezeStock);
            return stockVo;
        }
    }

    public void removeRedisStock(String itemSpecId) {
        log.info("下单失败，删除redis中库存 itemSpecId: {}", itemSpecId);
        String stockLock = String.format(RedisConstants.LOCK_STOCK_KEY, itemSpecId);
        RLock lock = redissonClient.getLock(stockLock);
        try {
            lock.lock(30, TimeUnit.SECONDS);
            log.info("商品下单失败，删除redis中库存加锁成功 itemSpecId: {}", itemSpecId);
            String hashKey = String.format(RedisConstants.STOCK_KEY, itemSpecId);
            redis.del(hashKey);
            log.info("商品下单失败，删除redis中库存执行成功 itemSpecId: {}", itemSpecId);
        } finally {
            lock.unlock();
            log.info("商品下单失败，删除redis中库存解锁成功 itemSpecId: {}", itemSpecId);
        }
    }
}
